# -*- coding: UTF-8 -*-

from MiniJeux.PartieMiniJeu import *
import random
import time
import math
import sys

#Constante pour les quatre directions
#N en binaire(00000001)
NORTH=1
#W en binaire(00000010)
WEST=2
#S en binaire(00000100)
SOUTH=4
#E en binaire(00001000)
EAST=8
#V en binaire(00010000) est dédié à savoir si la case est déjà était visitées
VISITED=16

#Classe qui crée un point(x,y)
class Point(): 
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def dprint(self,text):
        print(text,self.x,self.y)

#Classe qui permet de créer une grille, crer des murs ou les enlever       
class Labyrinthe():
    def __init__(self,hauteur,largeur):
        sys.setrecursionlimit(10000)
        self.hauteur = hauteur
        self.largeur = largeur
        self.grille = []
        self.entree = Point(0,0)

        for i in range(self.largeur):
            self.grille.append([])
            for j in range(self.hauteur):
                self.grille[i].append(j)
            
        for i in range(self.largeur):
            for j in range(self.hauteur):
                self.grille[i][j]=NORTH|SOUTH|EAST|WEST
        
        self.recursBacktracker(None,Point(0,0),1)

    def getLargeur(self):
        return self.largeur
        
    def getHauteur(self):
        return self.hauteur
        
    def getEntree(self):
        return self.entree
        
    def caseNonVisitee(self,pos):
        #if (pos.x >= 0 and pos.x <= self.largeur-1) and (pos.y >= 0 and pos.y <= self.hauteur-1):
        if (self.grille[pos.x][pos.y] & VISITED) != 0:
            return False
        return True
    
    def voisinNonVisite(self,pos):
        liste=[]
        
        #GAUCHE
        pv = Point(pos.x-1,pos.y)
        if pv.x >= 0 and pv.x <= self.largeur-1:
            if self.caseNonVisitee(pv):
                liste.append(pv)
                
        #DROITE
        pv = Point(pos.x+1,pos.y)
        if pv.x >= 0 and pv.x <= self.largeur-1:
            if self.caseNonVisitee(pv):
                liste.append(pv)
        #HAUT
        pv = Point(pos.x,pos.y+1)
        if pv.y >= 0 and pv.y <= self.hauteur-1:
            if self.caseNonVisitee(pv):
                liste.append(pv)
        #BAS
        pv = Point(pos.x,pos.y-1)
        if pv.y >= 0 and pv.y <= self.hauteur-1:
            if self.caseNonVisitee(pv):
                liste.append(pv)

        if len(liste) != 0:
            return liste[random.randrange(len(liste))]
        else:
            return None
        
    def enleverMurEntrePaire(self,pos1,pos2):
        if pos1.x - pos2.x == 0:
            if pos1.y > pos2.y:
                self.enleverMurs(pos1,SOUTH)
                self.enleverMurs(pos2,NORTH)
            else:
                self.enleverMurs(pos1,NORTH)
                self.enleverMurs(pos2,SOUTH)
        else:
            if pos1.x > pos2.x:
                self.enleverMurs(pos1,WEST)
                self.enleverMurs(pos2,EAST)
            else:
                self.enleverMurs(pos1,EAST)
                self.enleverMurs(pos2,WEST)
    
    def marquerVisite(self,pos):
        self.grille[pos.x][pos.y] |= VISITED
    
    def recursBacktracker(self,stack,courant,nbVisites):
        fini=False
        self.marquerVisite(courant)
        while not fini:
            voisin = self.voisinNonVisite(courant)
            if not voisin:
                fini=True
            else:
                self.enleverMurEntrePaire(courant,voisin)
                self.recursBacktracker(stack,voisin,nbVisites+1)

    def murNord(self,pos):
        if (self.grille[pos.x][pos.y] & NORTH) != 0:
            return True
        else:
            return False

    def murSud(self,pos):
        if (self.grille[pos.x][pos.y] & SOUTH) != 0:
            return True
        else:
            return False
            
    def murEst(self,pos):
        if (self.grille[pos.x][pos.y] & EAST) != 0:
            return True
        else:
            return False
            
    def murOuest(self,pos):
        if (self.grille[pos.x][pos.y] & WEST) != 0:
            return True
        else:
            return False
        
    def creerMurs(self,position,orientation):
        self.grille[position.x][position.y] = self.grille[position.x][position.y] | orientation
    
    def enleverMurs(self,position,orientation):
        self.grille[position.x][position.y] = self.grille[position.x][position.y] & (~orientation)
            
class Surprise():
    def __init__(self,modele,pos=Point(0,0),type="inconnu"):
        self.modele=modele
        self.pos=pos
        self.type=type
    def action(self,joueur):
        pass

class SurpriseDeplacerBiere(Surprise):
    def __init__(self,modele,pos):
        Surprise.__init__(self,modele,pos,type="deplacer")

    def action(self,joueur):
        print "deplacer biere"
        self.modele.deplacerBiere()

class SurpriseTeleporterJoueur(Surprise):
    def __init__(self,modele,pos):
        Surprise.__init__(self,modele,pos,type="teleporter")

    def distanceManhattan(self,p1,p2):
        return abs(p1.x-p2.x) + abs(p1.y-p2.y)
        
    def horsGrille(self,pos):
        if pos.x >= self.modele.lab.largeur or pos.x < 0:
            return True
        if pos.y >= self.modele.lab.hauteur or pos.y < 0:
            return True
        return False
                
    def action(self,joueur):
        print "trou noir"
        if self.distanceManhattan(self.modele.biere,joueur.position) > 1:
            dx=dy=0
            invalide=True
            
            while invalide:
                while dx==0 and dy==0:
                    dx = random.randrange(3)-1
                    dy = random.randrange(3)-1
                    nouvPos = Point(dx + self.modele.biere.x, dy + self.modele.biere.y)
                    invalide = self.horsGrille(nouvPos)
                    if not invalide:
                        joueur.position = nouvPos
                    else:
                        dx=dy=0

class SurpriseInterchangerJoueurs(Surprise):
    def __init__(self,modele,pos):
        Surprise.__init__(self,modele,pos,type="interchanger")

    def action(self,joueur):
        print "interchager joueurs"
        tabPos=[]
        for j in self.modele.joueurs.values():
            tabPos.append(j.position)
        random.shuffle(tabPos)
        i=0
        for nom in self.modele.joueurs.keys():
            self.modele.joueurs[nom].position = tabPos[i]
            i+=1

class SurpriseGelerJoueurs(Surprise):
    def __init__(self,modele,pos):
        Surprise.__init__(self,modele,pos,type="geler")

    def action(self,joueur):
        print "geler joueurs"
        for j in self.modele.joueurs.values():
            if j != joueur:
                j.gele = True
        self.modele.joueurNonGele = joueur
        self.modele.tempsGeleDepart = time.time()
        joueur.gele = False

class SurpriseInconnue(Surprise):
    def __init__(self,modele,pos):
        Surprise.__init__(self,modele,pos,type="inconnue")

    def action(self,joueur):
        rd=random.randrange(4)
        if rd == 0:
            surprise = SurpriseDeplacerBiere(self.modele,self.pos)
        elif rd == 1:
            surprise = SurpriseTeleporterJoueur(self.modele,self.pos)
        elif rd == 2:
            surprise = SurpriseInterchangerJoueurs(self.modele,self.pos)
        elif rd == 3:
            surprise = SurpriseGelerJoueurs(self.modele,self.pos)
        surprise.action(joueur)

#Model du mini jeu FrogLab
class ModelFrogLab(PartieMiniJeu):
    def __init__(self,listeJoueurs):
        PartieMiniJeu.__init__(self,"FrogLab",listeJoueurs)
        self.lab=None
        self.tempsDepart=None
        self.quitter=False
        self.tempsMax=60
        self.joueurNonGele=None
        self.tempsGeleDepart=time.time()-10000
        self.tempsGeleMax=3
        self.biere=None
        self.surprises=[]
        self.commence=False
        self.description = """Dans ce jeu, vous devez ramasser des bières:
 - Liste des power ups (appuyez sur Ctrl de gauche pour les activer):
    - Trou noir: Vous amène près de la bière.
    - Cycle: Permute la position des joueurs.
    - Cube de glace: Gèle tous les autres joueurs.
    - Bière avec la flèche: déplace la bière.
    - ? : Un parmi ceux plus haut.	"""

    def reset(self,listeJoueurs):
        ModelFrogLab.__init__(self,listeJoueurs)
        self.tempsMax=20
        self.commence=False
        self.tempsDepart = time.time()
        self.lab = Labyrinthe(12,12)
        for joueur in self.joueurs.values():
            joueur.quitter=False
            joueur.position = Point(self.lab.entree.x, self.lab.entree.y)
            joueur.direction = NORTH
            joueur.gele=False
        # Placer la biere et les surprises.
        self.biere = Point(self.lab.largeur-1,self.lab.hauteur-1)
        self.surprises.append(SurpriseDeplacerBiere(self,self.genererPosElemValide()))
        self.surprises.append(SurpriseTeleporterJoueur(self,self.genererPosElemValide()))
        self.surprises.append(SurpriseInterchangerJoueurs(self,self.genererPosElemValide()))
        self.surprises.append(SurpriseGelerJoueurs(self,self.genererPosElemValide()))
        self.surprises.append(SurpriseInconnue(self,self.genererPosElemValide()))
        print("FrogLab::reset()")
 
    def play(self):
        #partir le timer, etc.
        self.commence=True
        self.tempsDepart=time.time()
        print("FrogLab::play()")

    def update(self):
        if self.partieFinie():
            self.quitter=True

    def getBiere(self):
        return self.biere

    def getSurprises(self):
        liste=[]
        for s in self.surprises:
            liste.append([s.type, s.pos])
        return liste

    # Genere une position d'element qui ne chevauchent pas les autres.
    def genererPosElemValide(self):
        valide = False
        while not valide:
            valide = True
            x = random.randrange(0,self.lab.largeur-1)
            y = random.randrange(0,self.lab.hauteur-1)
            if self.biere.x == x and self.biere.y == y:
                valide = False
            for s in self.surprises:
                if s.pos.x == x and s.pos.y == y:
                    valide = False
        return Point(x,y)

    # Deplace la biere a une position au hasard.
    def deplacerBiere(self):
        self.biere = self.genererPosElemValide()

    def deplacerSurprise(self,idx):
        self.surprises[idx].pos = self.genererPosElemValide()

    def getLabyrinthe(self):
        return self.lab
        
    def getJoueursInfos(self):
        liste=[]
        for nom,joueur in self.joueurs.items():
            liste.append([nom,joueur.couleur,joueur.direction,joueur.position])
        return liste

    def getPositionJoueur(self,nomJoueur):
        return self.joueurs[nomJoueur].position
        
    def estGele(self,nomJoueur):
        if self.geleActif():
            return self.joueurs[nomJoueur].gele
        return False
        
    def getNomsJoueurs(self):
        liste=[]
        for nom in self.joueurs.keys():
            liste.append(nom)
        return liste
    
    def getDirection(self,nomJoueur):
        joueur = self.joueurs[nomJoueur]
        return joueur.direction
        
    def getDirections(self):
        dict={}
        for nom,joueur in self.joueurs.items():
            dict[nom]=joueur.direction
        return dict
    
    def getTempsRestant(self):
        return self.tempsMax - int(math.floor(time.time()-self.tempsDepart))

    #Méthode pour vérifier si le déplacement est possible dans une direction 
    def deplacementPossible(self,direction,joueur):
        case = self.lab.grille[joueur.position.x][joueur.position.y]
        #print(direction,joueur.position.x, joueur.position.y,case)
        if (direction & case) != 0:
            return False
        else:
            return True

    #Méthode qui se base sur la méthode deplacementPossible() 
    #et fait les déplacements nécessaires
    def deplacementValide(self,direction,joueur):
        joueur.direction = direction#garder la direction vers laquelle on se déplace
        if self.deplacementPossible(direction,joueur):
            if(direction == NORTH):
                joueur.position.y += 1
            if(direction == SOUTH):
                joueur.position.y -= 1
            if(direction == WEST):
                joueur.position.x -= 1
            if(direction == EAST):
                joueur.position.x += 1
        if self.biereAtteinte(joueur):
            self.deplacerBiere()
            joueur.score += 15
        #self.verifierSurprises(joueur)
       
       
    def geleActif(self):
        return self.tempsGeleMax - int(math.floor(time.time()-self.tempsGeleDepart)) > 0
        
    def deplacement(self,direction,nomJoueur):
        joueur = self.joueurs[nomJoueur]
        if self.geleActif():
            if joueur == self.joueurNonGele:
                self.deplacementValide(direction,joueur)
        else:
            self.deplacementValide(direction,joueur)

    def activerSurprise(self,nomJoueur):
        joueur = self.joueurs[nomJoueur]
        idx=0
        for s in self.surprises:
            if joueur.position.x == s.pos.x and joueur.position.y == s.pos.y:
                s.action(joueur)
                self.deplacerSurprise(idx)
            idx+=1

    def doitQuitter(self):
        return self.quitter

    # Cette fonction sera appele par le controleur une fois qu'il aura enlever les timers.
    def quitterPartie(self,nom):
        termine=True
        self.joueurs[nom].quitter=True
        for joueur in self.joueurs.values():
            joueur.quitter = True;
        self.termine=termine

    #Méthode qui vérifie est ce que un joueur à atteint le point de biere   
    def biereAtteinte(self,joueur):
        if(joueur.position.x == self.biere.x):
            if(joueur.position.y == self.biere.y):
                return True
        return False
    
    #La partie est fini quand le temps est expire.
    def partieFinie(self):
        compteur=0
        if self.getTempsRestant() == 0:
            return True
 
if __name__ == '__main__':        
    m = ModelFrogLab([])
    print(m.deplacementPossible(NORTH, m.joueurs[0]))
    print(m.deplacement(NORTH, m.joueurs[0]))
    print(m.biereAtteinte(m.joueurs[0]))
    print(m.partieFinie(m.joueurs[0]))
    print "Fini"
